package app;

//import org.apache.log4j.Logger;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.ListIterator;

import org.grlea.log.SimpleLogger;

import color.Color;
import color.Rgb;

import cluster.Observation;

import processing.core.*;
public class PixelReader {
	/**
	 * Logger for this class
	 */
	//private static final Logger logger = Logger.getLogger(PixelReader.class);

	private static final SimpleLogger log = new SimpleLogger(PixelReader.class);
	
	ArrayList<Observation<Rgb>> colors;
	PApplet pa;
	String file;
	public static int defaultMinFreqSizeRatio = 12400;
	public int minFreq;
	public int maxSize;
	ProgressListener listener = null;
	
	public PixelReader(PApplet pa, String file, int maxSize){
		this.pa = pa;
		this.file = file;
		this.maxSize = maxSize;
		//colors = getColors(pa, file);
		//colors = filterColors(colors, maxSize);
		///log.info("PixelReader loaded " + colors.size() + " colors. minFreq " + minFreq);
	}
	
	public PixelReader(PApplet pa, ProgressListener listener){
		this.pa = pa;
		this.listener = listener;

	}
	
	static float exceedRatio = 1f / 3f;
	
	public ArrayList<Observation<Rgb>> getColors(PApplet pa, String file){
		log.info("starting to load " + file);
		PImage image = pa.loadImage(file);
		//minFreq = Math.round(image.pixels.length / 12400);
		log.info("image loaded.");
		Arrays.sort(image.pixels);
		log.info("pixels sorted");
		ArrayList<Observation<Rgb>> colors = new ArrayList<Observation<Rgb>>();
		int prevClr = 0;
		boolean firstIteration = false;
		Observation<Rgb> current = null;
		int blipCount = 0;
		int blipInterval = 500;
		for(int i = 0; i < image.pixels.length; i++){
			if(blipCount % blipInterval == 0) {
				log.info("Reading : " + i + " of " + image.pixels.length + " pixels" );
				listener.progress("readColors", (float)i/image.pixels.length);
			}
			
			blipCount++;
			//pa.background(image.pixels[i]);
			int clr = image.pixels[i];
			if(firstIteration || clr != prevClr){
				if(current != null) {
					if(current.freq > 1){
						current.setFreqs(clr, getTotal(colors));
						colors.add(current);
						//log.info("new Color added: " + Integer.toHexString(prevClr) + " freq " + current.freq);
					}
				}
				current = new Observation<Rgb>(new Rgb(clr));
			}
			if(!firstIteration && clr == prevClr){
				current.addFreq();
			}
			prevClr = clr;
		}
		return colors;
	}

	
	public ArrayList<Rgb> getColorBins(ArrayList<Observation<Rgb>> list, int preferredSize, int binSize){
		ArrayList<PixelBin> pixelBins = new ArrayList<PixelBin>();
		Iterator<Observation<Rgb>> ri = list.iterator();
		int blipCount = 0;
		int blipInterval = 50;
		while(ri.hasNext()){
			if(blipCount % blipInterval == 0) {
				listener.progress("binColors", (float) blipCount / (float) list.size());
				log.info("making Bins : " + blipCount + 
						" of " + list.size() + " pixels BPLIPin " + pixelBins.size() + " bins" );
			}
			blipCount++;
			Observation<Rgb> clr = ri.next();
			Iterator<PixelBin> pbi = pixelBins.iterator();
			boolean exists = false;
			while(!exists && pbi.hasNext()){
				PixelBin bin = pbi.next();
				if(bin.insideBin(clr)){
					exists = true;
					bin.addColor(clr);
				}
			}
			if(!exists){
				//int d = (int)Math.floor(255f / (float)binSize);
				int r = (int)Math.floor(clr.obs.r / (float)binSize);
				int g = (int)Math.floor(clr.obs.g / (float)binSize);
				int b = (int)Math.floor(clr.obs.b / (float)binSize);
				PixelBin bin = new PixelBin(r * binSize, g * binSize, b * binSize, binSize);
				bin.addColor(clr);
				pixelBins.add(bin);
			}
		}

//		if (logger.isDebugEnabled()) {
//			logger
//					.debug("getColorBins(ArrayList<Observation<Rgb>>, int, int) - Bins made");
//		}

		Collections.sort(pixelBins);
//		if (logger.isDebugEnabled()) {
//			logger
//					.debug("getColorBins(ArrayList<Observation<Rgb>>, int, int) - Bins sorted");
//		}
		int numColors = pixelBins.size();
		ListIterator<PixelBin> pbi = null;
		pixelBins.size();
		if(pixelBins.size() > preferredSize){
			pbi = pixelBins.listIterator(preferredSize);
			if(!pbi.hasNext()) numColors = preferredSize;
			else{
				float freq = pbi.next().getAvgFreq(); 
				
				int after = 0;
				boolean changed = false;
				while(pbi.hasNext() && !changed){
					PixelBin next = pbi.next();
					if(next.getAvgFreq() == freq) after++;
					else changed = true;
				}
				pbi = pixelBins.listIterator(preferredSize);
				
				int before = 0;
				changed = false;
				while(pbi.hasPrevious() && !changed){
					PixelBin prev = pbi.previous();
					if(prev.getAvgFreq() == freq) before++;
					else changed = true;
				}
				
				if(after<before) numColors = preferredSize + after;
				else numColors = preferredSize - before;
			}
		}

//		if (logger.isDebugEnabled()) {
//			logger
//					.debug("getColorBins(ArrayList<Observation<Rgb>>, int, int) - numColors="
//							+ numColors + " preferredsize=" + preferredSize);
//		}
		
		pbi = pixelBins.listIterator();
		ArrayList<Rgb> colors = new ArrayList<Rgb>();
		int count = 0;
		while(pbi.hasNext() && count < numColors){
			colors.add(pbi.next().getColor());
			count++;
		}
//		if (logger.isDebugEnabled()) {
//			logger
//					.debug("getColorBins(ArrayList<Observation<Rgb>>, int, int) - colors added");
//		}
		return colors;
	}
	
	public static ArrayList<Observation<Rgb>> filterColors(ArrayList<Observation<Rgb>> colors, int maxSize){
		Collections.sort(colors);
		if(colors.size() <= maxSize) return colors;
		ListIterator<Observation<Rgb>> ci = colors.listIterator(colors.size() - maxSize);
		int freq = ci.next().freq;		
		int below = 0;
		int nextFreq = freq;
		while(ci.hasNext() && nextFreq == freq ){
			nextFreq = ci.next().freq;
			below++;
		}
		below--;
		int above = 0;
		ci = colors.listIterator(colors.size() - maxSize);
		nextFreq = freq;
		while(ci.hasPrevious() && nextFreq == freq ){
			nextFreq = ci.previous().freq;
			above++;
		}
		above--;
		int size;
		log.debugObject("above", above);
		log.debugObject("below", below);
		log.debugObject("above/below", (float) above / (float) below);
		if((float) above / (float) below < exceedRatio){
			size = maxSize + above;
			//minFreq = freq;
		}
		else {
			size = maxSize - below;
			//minFreq = freq - 1;
		}
		log.debugObject("size", size);
		return new ArrayList<Observation<Rgb>>(colors.subList(colors.size() - size -1, colors.size() - 1));
	}
	
	
	
	public static int getTotal(ArrayList<Observation<Rgb>> colors){
		int total = 0;
		Iterator<Observation<Rgb>> i = colors.iterator();
		while(i.hasNext()) total += i.next().freq;
		return total;
	}
	
	public void mergeColors(int dist){
		
		
		
	}
	
	public static ArrayList<Rgb> getRgbArrayFromObservation(ArrayList<Observation<Rgb>> list){
		Iterator<Observation<Rgb>> i = list.iterator();
		ArrayList<Rgb> rgb = new ArrayList<Rgb>(list.size());
		while(i.hasNext()) rgb.add(i.next().obs);
		return rgb;
	}
	
	public static ArrayList<Color> castToColor(ArrayList<? extends Color> list){
		ArrayList<Color> n = new ArrayList<Color>(list.size());
		Iterator<? extends Color> i = list.iterator();
		while(i.hasNext()) n.add((Color)i.next());
		return n;
	}
	
	
	
	
	
}
